﻿using System;
using ShureValidation.Language;

namespace ShureValidation.Rules
{
    /// <summary>
    /// Input may only be letters and digits, foreign chars (for instance åäö) etc are included.
    /// </summary>
    /// <para>
    /// Language file items:
    /// <list type="table">
    ///     <listheader>
    ///         <term>ItemName</term>
    ///         <description>Language text</description>
    ///     </listheader>
    ///     <item>
    ///         <term>LettersAndDigits</term>
    ///         <description>'{0}' may only contain letters and digits.</description>
    ///     </item>
    ///     <item>
    ///         <term>LettersAndDigitsExtra</term>
    ///         <description>'{0}' may only contain letters. digits and '{1}'.</description>
    ///     </item>
    /// </list>
    /// </para>
    [LanguagePrompt("LettersAndDigits", "'{0}' may only contain letters and digits.")]
    [LanguagePrompt("LettersAndDigitsExtra", "'{0}' may only contain letters, digits and '{1}'.")]
    [Serializable]
    public class LettersAndDigitsRule : IRule
    {
        /// <summary>
        /// Foreign characters (letters used for instance in the Swedish language)
        /// </summary>
        public const string ForeignCharacters = "åäëïöüâêîÛôãõÄËÏÖÜÂÊÎÔÛÃÕáéíóúÁÉÍÓÚàèìòùÀÈÌòÙ";

        /// <summary>
        /// Foreign characters (letters used for instance in the Swedish language) and space, dot and comma.
        /// </summary>
        public const string ForeignCharactersSDC = "åäëïöüâêîÛôãõÄËÏÖÜÂÊÎÔÛÃÕáéíóúÁÉÍÓÚàèìòùÀÈÌòÙ .,";

        /// <summary>
        /// Space, dot and comma.
        /// </summary>
        public const string SpaceCommaDot = " .,";


        /// <summary>
        /// Initializes a new instance of the <see cref="LettersAndDigitsRule"/> class.
        /// </summary>
        public LettersAndDigitsRule() : this(string.Empty)
        {
            ExtraCharacters = ForeignCharacters;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="LettersAndDigitsRule"/> class.
        /// </summary>
        /// <param name="extraCharacters">The extra characters.</param>
        /// <exception cref="ArgumentNullException"><c>extraCharacters</c> is <c>null</c>.</exception>
        public LettersAndDigitsRule(string extraCharacters)
        {
            if (extraCharacters == null)
                throw new ArgumentNullException("extraCharacters");

            ExtraCharacters = extraCharacters + ForeignCharacters;
        }

        /// <summary>
        /// Gets the extra characters.
        /// </summary>
        /// <value>The extra characters.</value>
        public string ExtraCharacters { get; private set; }

        #region IRule Members

        /// <summary>
        /// Determines if the validation support the specified type.
        /// </summary>
        /// <param name="type">Property/Value type.</param>
        /// <returns>true if type is supported.</returns>
        /// <remarks>
        /// Used when validation objects are generated.
        /// </remarks>
        public bool SupportsType(Type type)
        {
            return type == typeof (string);
        }

        /// <summary>
        /// Validate a field value
        /// </summary>
        /// <param name="value">Value to validate</param>
        /// <returns><c>true</c> if validation was successful; otherwise <c>false</c>.</returns>
        /// <exception cref="NotSupportedException">Only strings are valid for this type.</exception>
        public virtual bool Validate(object value)
        {
            if (ValueChecker.IsEmpty(value))
                return true;

            if (!(value is string))
                throw new NotSupportedException("Only strings are valid for this type.");

            var valueStr = (string) value;
            foreach (char ch in valueStr)
            {
                if (char.IsLetterOrDigit(ch) || ExtraCharacters.IndexOf(ch) != -1)
                    continue;

                return false;
            }

            return true;
        }

        /// <summary>
        /// Format an error message
        /// </summary>
        /// <param name="fieldName">Field (have already been translated)</param>
        /// <param name="rulesLanguage">Language prompts for validation rules</param>
        /// <returns>Error message formatted for the current language.</returns>
        public virtual string Format(string fieldName, IModelLanguage rulesLanguage)
        {
            return ExtraCharacters != null
                       ? string.Format(rulesLanguage["LettersAndDigits"], fieldName)
                       : string.Format(rulesLanguage["LettersAndDigitsExtra"], fieldName);
        }

        #endregion
    }
}